package integ

import (
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/klogsdk/klog-go-sdk/klog"
	"io/ioutil"
	"net/http"
	"os/exec"
	"time"
)

var server *mockServer

type mockServer struct {
	ctx        context.Context
	cancel     context.CancelFunc
	stopWait   chan interface{}
	httpClient *http.Client
}

type poolStat struct {
	Requests int `json:"requests"`
	Batches  int `json:"batches"`
	Contents int `json:"contents"`
	Logs     int `json:"logs"`
	C200     int `json:"200"`
	C400     int `json:"400"`
	C500     int `json:"500"`
}

func newMockServer() {
	server = &mockServer{
		stopWait: make(chan interface{}),
		httpClient: &http.Client{
			Timeout: time.Duration(10) * time.Second,
		},
	}
}

func (o *mockServer) start() {
	ctx, cancel := context.WithCancel(context.Background())
	o.ctx = ctx
	o.cancel = cancel

	cmd := exec.CommandContext(ctx, "python", "-m", "klog.mock")
	go func() {
		_ = cmd.Run()
		o.stopWait <- struct{}{}
	}()

	for {
		if _, err := o.getStat(); err != nil {
			time.Sleep(time.Millisecond * time.Duration(10))
		} else {
			break
		}
	}
}

func (o *mockServer) stop() {
	o.cancel()
	<-o.stopWait
}

func (o *mockServer) clearStat() {
	_, _ = o.doRequest("/_clear_stat")
}

func (o *mockServer) getPoolStat(prj, pool string) poolStat {
	stat := poolStat{}
	if stats, err := o.getStat(); err == nil {
		if s, ok := stats[fmt.Sprintf("%s__%s", prj, pool)]; ok {
			stat = s
		}
	}
	return stat
}

func (o *mockServer) getStat() (map[string]poolStat, error) {
	stats := make(map[string]poolStat)
	if data, err := o.doRequest("/_stat"); err != nil {
		return nil, err
	} else if err := json.Unmarshal(data, &stats); err != nil {
		return nil, err
	} else {
		return stats, nil
	}
}

func (o *mockServer) setModeInternalServerError() {
	_, _ = o.doRequest("/_set_mode?mode=" + klog.InternalServerError)
}

func (o *mockServer) setModeNormal() {
	_, _ = o.doRequest("/_set_mode?mode=normal")
}

func (o *mockServer) setMode(mode string) {
	_, _ = o.doRequest("/_set_mode?mode=" + mode)
}

func (o *mockServer) doRequest(path string) ([]byte, error) {
	if req, err := http.NewRequest("POST", "http://127.0.0.1:8765"+path, nil); err != nil {
		return nil, err
	} else if response, err := o.httpClient.Do(req); err != nil {
		return nil, err
	} else if response.StatusCode != 200 {
		return nil, fmt.Errorf("mock server status code=%d", response.StatusCode)
	} else {
		if body, err := ioutil.ReadAll(response.Body); err != nil {
			return nil, err
		} else {
			_ = response.Body.Close()
			return body, nil
		}
	}
}
